package com.neusoft.hifly.commons.lang;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.commons.lang3.StringUtils;

/**
 * Title: 核心类库
 * <p>
 * Description: 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精 确的浮点数运算，包括加减乘除和四舍五入。
 * <p>
 * Copyright: 2019
 * <p>
 * Company: 东软汉枫
 *
 * @author lh_neu
 * @version 1.0.0
 */
public class Arith {
	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 10;

	/**
	 * 这个类不能实例化
	 */
	private Arith() {
	}

	/**
	 * 提供精确的加法运算。例如：
	 * <p>
	 * Input -> v1 = 123.1234567 , v2 = 987.54321
	 * <p>
	 * Output -> 1110.6666667
	 *
	 * @param v1
	 *            被加数
	 * @param v2
	 *            加数
	 * @return 两个参数的和
	 */
	public static double add(final double v1, final double v2) {
		final BigDecimal b1 = new BigDecimal(Double.toString(v1));
		final BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.add(b2).doubleValue();
	}

	/**
	 * 提供精确的减法运算。例如：
	 * <p>
	 * Input -> v1 = 987.54321 , v2 = 123.1234567
	 * <p>
	 * Output -> 864.4197533
	 *
	 * @param v1
	 *            被减数
	 * @param v2
	 *            减数
	 * @return 两个参数的差
	 */

	public static double sub(final double v1, final double v2) {
		final BigDecimal b1 = new BigDecimal(Double.toString(v1));
		final BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * 提供精确的乘法运算。例如：
	 * <p>
	 * Input -> v1 = 12.34 , v2 = 56.78
	 * <p>
	 * Output -> 700.6652
	 *
	 * @param v1
	 *            被乘数
	 * @param v2
	 *            乘数
	 * @return 两个参数的积
	 */
	public static double mul(final double v1, final double v2) {
		final BigDecimal b1 = new BigDecimal(Double.toString(v1));
		final BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.multiply(b2).doubleValue();
	}

	/**
	 * 提供（相对）精确的除法运算
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 四舍五入取整
	 */
	public static long divToLong(final double v1, final double v2) {
		return new BigDecimal(Arith.div(v1, v2)).setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
	}

	/**
	 * 提供（相对）精确的除法运算
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 保留两位小数
	 */
	public static float divScale2(final double v1, final double v2) {
		return (float) Arith.div(v1, v2, 2);
	}

	/**
	 * 提供（相对）精确的除法运算
	 * <p>
	 * 当发生除不尽的情况时，精确到 小数点以后10位，以后的数字四舍五入。例如：
	 * <p>
	 * Input -> v1 = 12.34 , v2 = 56.78
	 * <p>
	 * Output -> 0.2173300458
	 *
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div(final double v1, final double v2) {
		if (v1 == 0 || v2 == 0) {
			return 0;
		}
		return Arith.div(v1, v2, Arith.DEF_DIV_SCALE);
	}

	/**
	 * 提供（相对）精确的除法运算
	 * <p>
	 * 当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。例如：
	 * <p>
	 * Input -> v1 = 12.34 , v2 = 56.78
	 * <p>
	 * Output -> 0.217
	 *
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示表示需要精确到小数点以后几位。
	 * @return 两个参数的商
	 */
	public static double div(final double v1, final double v2, final int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		if (v1 == 0 || v2 == 0) {
			return 0;
		}
		final BigDecimal b1 = new BigDecimal(Double.toString(v1));
		final BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理。例如：
	 * <p>
	 * Input -> v1 = 12.3456789 , scale = 3
	 * <p>
	 * Output -> 12.346
	 *
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double round(final double v, final int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		final BigDecimal b = new BigDecimal(Double.toString(v));
		final BigDecimal one = new BigDecimal("1");
		return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 计算百分比
	 * <p>
	 * Input -> p1 = 1 , p2 = 3
	 * <p>
	 * Output -> 33.33%
	 * <p>
	 * Input -> p1 = 2 , p2 = 4
	 * <p>
	 * Output -> 50.00%
	 *
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static String percent(final double p1, final double p2) {
		String str;
		final double p3 = p1 / p2;
		final NumberFormat nf = NumberFormat.getPercentInstance();
		nf.setMinimumFractionDigits(2);
		str = nf.format(p3);
		return str;
	}

	/**
	 * 计算器
	 * <p>
	 * 将字符串进行转换并计算
	 *
	 * @param str
	 *            字符串
	 * @param scale
	 *            小数点后保留几位
	 * @return 计算结果
	 */
	public static double calculator(final String str, final int scale) {
		if (StringUtils.isEmpty(str)) {
			return 0;
		}
		final ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
		try {
			return Arith.round((Double) jse.eval(str), scale);
		} catch (final ScriptException e) {
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * 测试
	 *
	 * @param args
	 *            参数
	 */
	public static void main(final String[] args) {
		System.out.println(divScale2(100, 0.25));
		// 计算器
		final String str = "(2+3.5)*3+9*6/8+(2+4/3.0+(2+6/5))";
		System.out.println(Arith.calculator(str, 2));

		final DecimalFormat nf = (DecimalFormat) NumberFormat.getInstance();
		//	nf.setMaximumIntegerDigits(arg0); // 设置树的整数部分所允许的最大位数
		//	nf.setMaximumFractionDigits(arg0); // 设置数的小数部分所允许的最大位数
		//		nf.setMinimumIntegerDigits(16); // 设置树的整数部分所允许的最小位数
		nf.setMinimumFractionDigits(2); // 设置数的小数部分所允许的最小位数
		nf.setGroupingUsed(false); // 使用逗号分组
		nf.setParseBigDecimal(false);
		System.out.println(nf.format(10000000.00));

		final int a = (int) Arith.div(100, 0.25, 0);
		System.out.println(a);
		//		System.out.println((int) Math.ceil(Arith.div(10, 0)));
		//		System.out.println("test1: " + percent(1, 3));
		//		System.out.println("test2: " + percent(2, 4));

		//		System.out.println((int) Math.ceil(Arith.div(25, 12)));
		//
		//		final double i = 3.123;
		//
		//		// 舍掉小数取整
		//		System.out.println("舍掉小数取整:Math.floor(" + i + ")=" + (int) Math.floor(i));
		//
		//		// 四舍五入取整
		//		System.out.println("四舍五入取整:(" + i + ")=" + new BigDecimal(i).setScale(0, BigDecimal.ROUND_HALF_UP));
		//
		//		// 四舍五入保留两位小数
		//		System.out.println("四舍五入取整:(" + i + ")=" + new BigDecimal(i).setScale(2, BigDecimal.ROUND_HALF_UP));
		//
		//		// 凑整，取上限
		//		System.out.println("凑整:Math.ceil(" + i + ")=" + (int) Math.ceil(i));
		//
		//		// 舍掉小数取整
		//		System.out.println("舍掉小数取整:Math.floor(-" + i + ")=" + (int) Math.floor(-i));
		//
		//		// 四舍五入取整
		//		System.out.println("四舍五入取整:(-" + i + ")=" + new BigDecimal(-i).setScale(0, BigDecimal.ROUND_HALF_UP));
		//
		//		// 四舍五入保留两位小数
		//		System.out.println("四舍五入取整:(-" + i + ")=" + new BigDecimal(-i).setScale(2, BigDecimal.ROUND_HALF_UP));
		//
		//		// 凑整，取上限
		//		System.out.println("凑整(-" + i + ")=" + (int) Math.ceil(-i));

	}
}